package com.open.lock.aspect;

import com.open.lock.annotation.Lock;
import com.open.lock.component.CloudLock;
import com.open.lock.enums.LockModel;
import com.open.lock.utils.LockUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;

/**
 * @author zhang kaichao
 */
@Aspect
public class LockAspect {

    @Autowired
    private CloudLock cloudLock;

    @Pointcut("@annotation(com.open.lock.annotation.Lock)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object aroundPointCut(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        assert requestAttributes != null;
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Lock lock = method.getAnnotation(Lock.class);
        String[] keys = lock.keys();
        LockModel lockModel = lock.lockModel();
        String[] keyList = LockUtil.createLockKey(joinPoint, keys);
        long attemptTimeout = lock.attemptTimeout();
        long leaseTime = lock.leaseTime();
        RLock rLock = cloudLock.tryLock(lockModel, attemptTimeout, leaseTime, keyList);
        try {
            return joinPoint.proceed();
        } finally {
            cloudLock.unlock(rLock);
        }
    }

}
